{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Pets example \n",
    "\n",
    "## Event based approach\n",
    "\n",
    "Let's simulate the easy life of a pet in the morning. First we define some data structures for pets:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "using DiscreteEvents, Random, Printf\n",
    "\n",
    "abstract type PetState end\n",
    "struct Sleeping  <: PetState end\n",
    "struct Scuffing  <: PetState end\n",
    "struct Running   <: PetState end\n",
    "\n",
    "abstract type PetEvent end\n",
    "struct GetWeary  <: PetEvent end\n",
    "struct GetHungry <: PetEvent end\n",
    "struct Scuff     <: PetEvent end\n",
    "struct LeapUp    <: PetEvent end\n",
    "struct Sleep     <: PetEvent end\n",
    "\n",
    "mutable struct Pet\n",
    "    clk::Clock\n",
    "    name::String\n",
    "    state::PetState\n",
    "    speak::String\n",
    "end"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Then we need an API for Pet since we do not want to access its state directly."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "setstate! (generic function with 1 method)"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "state(p) = p.state\n",
    "speak(p, n) = @printf(\"%5.2f %s: %s\\n\", tau(p.clk), p.name, p.speak^n)\n",
    "\n",
    "function setstate!(p::Pet, q::PetState)\n",
    "    p.state = q\n",
    "    @printf(\"%5.2f %s: %s\\n\", tau(p.clk), p.name, repr(p.state))\n",
    "end"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We describe the behaviour of the pet with some transition functions:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "doit! (generic function with 6 methods)"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "function doit!(p::Pet, ::Sleeping, ::LeapUp)   # leap up after sleeping\n",
    "    setstate!(p, Running())\n",
    "    event!(p.clk, fun(doit!, p, fun(state, p), GetHungry()), after, 5*rand())\n",
    "end\n",
    "\n",
    "function doit!(p::Pet, ::Scuffing, ::LeapUp)   # leap up while scuffing\n",
    "    setstate!(p, Running())\n",
    "    event!(p.clk, fun(doit!, p, fun(state, p), GetWeary()), after, 2*rand())\n",
    "end\n",
    "\n",
    "function doit!(p::Pet, ::Running, ::GetHungry) # get hungry while running\n",
    "    speak(p, 5)\n",
    "    event!(p.clk, fun(doit!, p, fun(state, p), Scuff()), after, rand())\n",
    "end \n",
    "\n",
    "function doit!(p::Pet, ::Running, ::GetWeary)  # get weary while running\n",
    "    speak(p, 2)\n",
    "    event!(p.clk, fun(doit!, p, fun(state, p), Sleep()), after, 2*rand())\n",
    "end \n",
    "\n",
    "function doit!(p::Pet, ::Running, ::Scuff)     # scuff after running\n",
    "    setstate!(p, Scuffing())\n",
    "    event!(p.clk, fun(doit!, p, fun(state, p), LeapUp()), after, 2*rand())\n",
    "end\n",
    "\n",
    "function doit!(p::Pet, ::Running, ::Sleep)     # sleep after running\n",
    "    setstate!(p, Sleeping())\n",
    "    event!(p.clk, fun(doit!, p, fun(state, p), LeapUp()), after, 10*rand())\n",
    "end"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Then we setup a simulation clock and a pet, schedule the first event and run it:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      " 5.00 Snoopy: Running()\n",
      " 8.84 Snoopy: huffhuffhuffhuffhuff\n",
      " 9.78 Snoopy: Scuffing()\n",
      "11.13 Snoopy: Running()\n",
      "11.92 Snoopy: huffhuff\n",
      "12.55 Snoopy: Sleeping()\n",
      "19.17 Snoopy: Running()\n",
      "22.10 Snoopy: huffhuffhuffhuffhuff\n",
      "22.16 Snoopy: Scuffing()\n",
      "22.69 Snoopy: Running()\n",
      "22.91 Snoopy: huffhuff\n",
      "23.24 Snoopy: Sleeping()\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "\"run! finished with 12 clock events, 0 sample steps, simulation time: 25.0\""
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "clk = Clock()\n",
    "Random.seed!(123)\n",
    "snoopy = Pet(clk, \"Snoopy\", Sleeping(), \"huff\")\n",
    "event!(clk, fun(doit!, snoopy, snoopy.state, LeapUp()), after, 5)\n",
    "run!(clk, 25)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Processes and implicit events\n",
    "\n",
    "`DiscreteEvents` provides us also with another approach: process-based simulation. In this case we implement the pet behaviour in a single function. For such a simple example this comes out simpler and more convenient:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "pet (generic function with 1 method)"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "function pet(clk::Clock, p::Pet)\n",
    "    setstate!(p, Running());  delay!(clk,  5*rand())\n",
    "    speak(p, 5);              delay!(clk,    rand())  # get hungry\n",
    "    setstate!(p, Scuffing()); delay!(clk,  2*rand())\n",
    "    setstate!(p, Running());  delay!(clk,  2*rand())\n",
    "    speak(p, 2);              delay!(clk,  2*rand())  # get weary\n",
    "    setstate!(p, Sleeping()); delay!(clk, 10*rand())\n",
    "end"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This describes one pet cycle. After each status change the pet function `delay!`s (gets suspended) for a given timeout on the clock. \n",
    "\n",
    "We have to reimplement our `speak` and `setstate!` functions since now we print from an asynchronous process. With `now!` we let the clock do the printing: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "setstate! (generic function with 1 method)"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "speak(p, n) = now!(p.clk, fun(println, @sprintf(\"%5.2f %s: %s\", tau(p.clk), p.name, p.speak^n)))\n",
    "\n",
    "function setstate!(p::Pet, q::PetState)\n",
    "    p.state = q\n",
    "    now!(p.clk, fun(println, @sprintf(\"%5.2f %s: %s\", tau(p.clk), p.name, repr(p.state))))\n",
    "end"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In order to make this work we have to register the pet function to the clock."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      " 5.00 Snoopy: Sleeping()\n",
      " 5.00 Snoopy: Running()\n",
      " 8.84 Snoopy: huffhuffhuffhuffhuff\n",
      " 9.78 Snoopy: Scuffing()\n",
      "11.13 Snoopy: Running()\n",
      "11.92 Snoopy: huffhuff\n",
      "12.55 Snoopy: Sleeping()\n",
      "19.17 Snoopy: Running()\n",
      "22.10 Snoopy: huffhuffhuffhuffhuff\n",
      "22.16 Snoopy: Scuffing()\n",
      "22.69 Snoopy: Running()\n",
      "22.91 Snoopy: huffhuff\n",
      "23.24 Snoopy: Sleeping()\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "\"run! finished with 24 clock events, 0 sample steps, simulation time: 25.0\""
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "resetClock!(clk, t0=5)              # reset the clock, we start at 5\n",
    "Random.seed!(123)                   # reseed the random generator\n",
    "setstate!(snoopy, Sleeping())       # set snoopy sleeping\n",
    "process!(clk, Prc(1, pet, snoopy));\n",
    "run!(clk, 20)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We got the same output – with more events for the `delay!` and `now!` calls)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Evaluation\n",
    "\n",
    "There is no point in doing simulations with such simple sequential examples, but if we do the same with more pets operating in parallel, things get messy very quickly and there is no way to code it sequentially. For every different random number seed we get different sequences of events. If we want to simulate that, we need parallel state machines, processes, actors … and their coordination on a time line. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Julia 1.3.1",
   "language": "julia",
   "name": "julia-1.3"
  },
  "language_info": {
   "file_extension": ".jl",
   "mimetype": "application/julia",
   "name": "julia",
   "version": "1.3.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
